/******************************************************************************
*  author: enquanyan
*  version: 1.0
*  description:
*  			entry module;
*  			initialize listening socket and thread pool;
*  			perform epoll_wait;
*  date: 2014-07-21
*  other: 无
******************************************************************************/
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <cassert>
#include <sys/epoll.h>
#include <signal.h>

#include "locker.h"
#include "threadpool.h"
#include "http_conn.h"
#include "def.h"
#include "util.h"

#define MAX_FD 1024
#define MAX_EVENT_NUMBER 1024
#define THREAD_NUM  4
#define MAX_REQUEST  1024
#define INTERVAL 5
#define LISTEN_NUM 512

extern int addfd(int epollfd, int fd, bool one_shot);

extern int addfd(int epollfd, abstract_conn *abstract_conn_ptr, int fd, bool one_shot);

//int log_file_fd = -1;
//const char *log_file = "/home/yeq/log/httpsvr/httpsvr_error.log";

//int monitor_log_file_fd = -1;
//const char *monitor_log_file = "/home/yeq/log/httpsvr/httpsvr_monitor.log";

const char * const config_file_path ="filehttpsvr.conf";

/*
void* monitor(void *interval)
{
	int interval_time = (int)interval;
	thread_arg *thread_arg_pointer = NULL;
	
	while(true)
	{
		fprintf(stderr, "\n thread_no \t received_bytes \t handled_http_conn \t active_http_conn \n");
		
		int active_http_conn = -100;
		
		for(int i = 0; i < THREAD_NUM; i++)
		{
			thread_arg_pointer = threadpool<abstract_conn>::thread_arg_array + i;
			
			active_http_conn = thread_arg_pointer->active_http_conn.load(std::memory_order_seq_cst);
			
			if(thread_arg_pointer->is_first)
			{
				fprintf(stderr, "%d\t%llu\t%llu\t%d\n",i, thread_arg_pointer->interval_received_bytes_first, thread_arg_pointer->interval_handled_http_conn_first, active_http_conn);
				thread_arg_pointer->interval_received_bytes_second = 0;
				thread_arg_pointer->interval_handled_http_conn_second = 0;
				thread_arg_pointer->is_first  = false;
			}
			else
			{
				fprintf(stderr, "%d\t%llu\t%llu\t%d\n",i, thread_arg_pointer->interval_received_bytes_second, thread_arg_pointer->interval_handled_http_conn_second, active_http_conn);
				thread_arg_pointer->interval_received_bytes_first = 0;
				thread_arg_pointer->interval_handled_http_conn_first = 0;
				thread_arg_pointer->is_first  = true;
			}
		}
		
		sleep(interval_time);
	}
	
	return NULL;
}
*/

void addsig(int sig, void (handler)(int), bool restart = true)
{
	struct sigaction sa;
	memset(&sa, '\0', sizeof(sa));
	sa.sa_handler = handler;
	if (restart) {
		sa.sa_flags |= SA_RESTART;
	}
	sigfillset(&sa.sa_mask);
	assert(sigaction( sig, &sa, NULL ) != -1);
}

void show_error(int connfd, const char* info)
{
	printf("%s", info);	
	send(connfd, info, strlen(info), 0);
	close(connfd);
}

static void sighandler(int sig_no)
{
    exit(0);
}

int main(int argc, char* argv[])
{
    signal(SIGUSR1,sighandler);
    
    /*
	if (argc <= 2)
	{
		printf("usage: %s ip_address port_number\n", basename(argv[0]));
		return 1;
	}
	*/
    
	//log_file_fd = open(log_file, O_RDWR|O_CREAT, 0666);
	//assert(log_file_fd > 0);
	
	//dup2(log_file_fd, 1);
	//close(log_file_fd);
	
	//monitor_log_file_fd = open(monitor_log_file, O_RDWR|O_CREAT, 0666);
	//assert(monitor_log_file_fd > 0);
	
	//dup2(monitor_log_file_fd, 2);
	//close(monitor_log_file_fd);
	
	//const char* ip = argv[1];
	//int port = atoi(argv[2]);
    
    std::map<std::string, std::string> config_map;
    assert(set_param(config_file_path, config_map) == true);

	addsig(SIGPIPE, SIG_IGN);

	//int user_count = 0;

	int listenfd = socket( PF_INET, SOCK_STREAM, 0);
	assert(listenfd >= 0);
	
	//struct linger tmp = { 1, 5 };
	struct linger tmp = { 0, 0 };
	setsockopt(listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));

	struct sockaddr_in address;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	//inet_pton( AF_INET, ip, &address.sin_addr);
    address.sin_addr.s_addr = htonl(INADDR_ANY);
	address.sin_port = htons(LOCAL_PORT);
	
	int ret = 0;
	ret = bind(listenfd, (struct sockaddr*) &address, sizeof(address));
	assert(ret >= 0);

	ret = listen(listenfd, LISTEN_NUM);
	assert(ret >= 0);
	
	//fork();
	//fork();
    
    daemon(0,0);
	
	threadpool<abstract_conn>* pool = NULL;
	try {
		pool = new threadpool<abstract_conn>(THREAD_NUM, MAX_REQUEST);
	} catch (...) {
		return 1;
	}
	
	//run monitor thread
	/*pthread_t monitor_pt;
	int interval_time = INTERVAL;
	if (pthread_create(&monitor_pt, NULL, monitor, (void*)interval_time) != 0)
	{
			exit(-1);
	}*/
	
	http_conn* users = new http_conn[MAX_FD];
	assert(users != NULL);
	
	epoll_event events[MAX_EVENT_NUMBER];
    
	int epollfd = epoll_create(200);
	assert(epollfd != -1);

	http_conn::m_epollfd = epollfd;
    
    forward_conn::m_epollfd = epollfd;
    
	addfd(epollfd, users + listenfd, listenfd, false);
	
	struct timezone tz;

	while (true) {
		
		int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
		
		if ((number < 0) && ( errno != EINTR))
		{
			pr_debug("epoll failure\n");
			break;
		}

		for (int i = 0; i < number; i++)
		{
			struct timeval test_time;
			gettimeofday(&test_time,&tz);
			
            abstract_conn *abstract_conn_ptr = (abstract_conn*)events[i].data.ptr;
			
			if (abstract_conn_ptr->m_sockfd == listenfd)
			{
                struct timeval test_time;
				gettimeofday(&test_time,&tz);
				
				int thread_no = 0;
				
				struct sockaddr_in client_address;
				
				socklen_t client_addrlength = sizeof(client_address);
				
				int connfd = -1;
				while((connfd = accept(listenfd,
						(struct sockaddr*) &client_address, &client_addrlength))> 0)
				{
                        thread_no = connfd%THREAD_NUM;
						 
						//pr_debug("accept->sockfd:%d->thread_no:%d\n", connfd, thread_no);
						 
						//fprintf(stderr, "accept->sockfd:%d->time:%ld\n",connfd, test_time.tv_sec);
                        
						abstract_conn *abstract_conn_temp_ptr = users + connfd;
                        
						abstract_conn_temp_ptr->thread_no = thread_no;
						abstract_conn_temp_ptr->init(connfd, client_address);
						
						threadpool<abstract_conn>::thread_arg_array[thread_no].active_http_conn++;
						
						int active_http_conn = threadpool<abstract_conn>::thread_arg_array[thread_no].active_http_conn.load(std::memory_order_seq_cst);
						
						//pr_debug("accept->sockfd:%d->thread_no:%d->--active_http_conn:%d\n", connfd, thread_no, active_http_conn);
				}
                
				continue;
			}

			if (events[i].events & ( EPOLLRDHUP | EPOLLHUP | EPOLLERR))
			{
				pr_debug("epoll_wait->EPOLLRDHUP|EPOLLHUP|EPOLLERR->sockfd:%d->thread_no:%d\n", abstract_conn_ptr->m_sockfd, abstract_conn_ptr->thread_no);
                
                abstract_conn_ptr->close_conn();

				continue;
			}

			if (events[i].events & EPOLLIN)
			{
				//fprintf(stderr, "EPOLLIN->sockfd:%d->time:%ld\n",sockfd, test_time.tv_sec);
				//pr_debug("EPOLLIN->sockfd:%d\n",abstract_conn_ptr->m_sockfd);
				abstract_conn_ptr->handle_type = READ;
				abstract_conn_ptr->is_read_ready = true;
				pool->append(abstract_conn_ptr, abstract_conn_ptr->thread_no);
				continue;
			}

			if (events[i].events & EPOLLOUT)
			{
				//fprintf(stderr, "EPOLLOUT->sockfd:%d->time:%ld\n",sockfd, test_time.tv_sec);
				//pr_debug("EPOLLOUT->sockfd:%d\n",abstract_conn_ptr->m_sockfd);
				abstract_conn_ptr->handle_type = WRITE;
				pool->append(abstract_conn_ptr, abstract_conn_ptr->thread_no);
			}
			
		}

	}

	close(epollfd);
	close(listenfd);
	delete[] users;
	delete pool;
	return 0;
}
